package it.antonio.ilpizze.func.core;

import it.antonio.ilpizze.func.Mapper;
import it.antonio.ilpizze.func.Order;
import it.antonio.ilpizze.func.Predicate;
import it.antonio.ilpizze.func.Reducer;
import it.antonio.ilpizze.func.Visit;
import it.antonio.ilpizze.func.Worker;
import it.antonio.ilpizze.func.util.InternalUtils;

import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;

public abstract class Collections extends Iterables implements Core{
	@Override
	public void mustImplementCollections() {
	}
	
	public static <T> void add(final Collection<T> collection, T...elems){
		java.util.Collections.addAll(collection, elems);
	}
	
	public static <T> boolean all(final Collection<T> collection, Predicate<? super T> pred) {
		for(T element: collection) {
			if(!pred.match(element)) return false;
		}
		return  true;
	}
	
	public static <T> boolean any(final Collection<T> collection, Predicate<? super T> pred) {
		for(T element: collection) {
			if(pred.match(element)) return true;
		}
		return false;
	}
	public static  <T> Collection<T> concat(final Collection<T> first, final Collection<T> second){
		return new AbstractCollection<T>() {

			@Override
			public Iterator<T> iterator() {
				return Iterators.concat(first.iterator(),second.iterator());
			}

			@Override
			public int size() {
				return first.size() +  second.size();
			}
		};
	}
	public static  <T> Collection<T> cycle(final Collection<T> collection) {
		return new AbstractCollection<T>() {

			@Override
			public Iterator<T> iterator() {
				return Iterables.cycle(collection).iterator();
			}

			@Override
			public int size() {
				throw new UnsupportedOperationException("Cycle Collection");
			}
		};
	}
	public static <T> Collection<T> filter(final Collection<T> collection, final Predicate<? super T> pred){
		
		return new AbstractCollection<T>() {

			@Override
			public Iterator<T> iterator() {
				return collection.iterator();
			}

			@Override
			public int size() {
				return collection.size();
			}
		};
	}
	public static <T> boolean find(final Collection<T> collection, Predicate<? super T> pred) {
		for(T element: collection) {
			if(!pred.match(element)) return false;
		}
		return  true;
	}
	
	public static <T> void forEach(final Collection<T> collection, Worker<? super T> worker) {
		for(T element: collection) worker.on(element);
	}

	public static <T> int frequency(final Collection<T> collection, T elem){
		return java.util.Collections.frequency(collection, elem);
	}

	public static  <T,Y> Collection<Y> map(final Collection<T> collection, final Mapper<? super T, ? extends Y> mapper){
		return new AbstractCollection<Y>() {

			@Override
			public Iterator<Y> iterator() {
				return Iterators.map(collection.iterator(), mapper);
			}

			@Override
			public int size() {
				return collection.size();
			}
		};
	}
	
	
	public static <T> T max(final Collection<T> collection, Order<T> order) {
		return java.util.Collections.max(collection, InternalUtils.comparator(order));
	}

	
	public static <T> T min(final Collection<T> collection, Order<T> order) {
		return java.util.Collections.min(collection, InternalUtils.comparator(order));
	}
	
	public static <T, R> R reduce(final Collection<T> collection, R start, Reducer<R, T> reducer) {
		R accumulator = start;
		for(T element: collection) {
			accumulator = reducer.reduce(accumulator, element);
		}
		return accumulator;
	}
	
	
	public static  <T> Collection<T> synchronize(final Collection<T> collection) {
		return java.util.Collections.synchronizedCollection(collection);
		
	}
	public static  <T> Collection<T> truncate(final Collection<T> collection, final int size) {
		
		return new AbstractCollection<T>() {

			@Override
			public Iterator<T> iterator() {
				return Iterators.truncate(collection.iterator(), size);
			}

			@Override
			public int size() {
				int dSize = collection.size();
				return dSize < size ? dSize : size;
			}
		};
	}
	
	public static  <T> Collection<T> unmodifiable(final Collection<T> collection) {
		return java.util.Collections.unmodifiableCollection(collection);
		
	}
	
	public static <T> void visit(final Collection<T> collection, Visit<? super T> visit) {
		for(T element: collection) {
			if(visit.on(element) == Visit.STOP) break; 
		}
	}
}
